package org.zlui65.milan

import android.content.res.TypedArray
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.annotation.Px
import androidx.core.content.ContextCompat
import org.zlui65.milan.annotations.Attr
import org.zlui65.milan.annotations.Styleable
import org.zlui65.milan.annotations.StyleableChild
import java.lang.reflect.Field
import java.lang.reflect.Method

/**
 * Utility class for applying styles to views.
 */
internal object StyleApplier {

    /**
     * Applies an AttributeSet to a view.
     */
    fun applyAttributeSet(view: View, attrs: AttributeSet) {
        val styleableAnnotation = view.javaClass.getAnnotation(Styleable::class.java) ?: return
        val styleableName = styleableAnnotation.value
        
        val context = view.context
        val resources = context.resources
        val packageName = context.packageName
        
        // Get the styleable resource ID
        val styleableId = resources.getIdentifier(styleableName, "styleable", packageName)
        if (styleableId == 0) return
        
        // Get the typed array
        val typedArray = context.obtainStyledAttributes(attrs, intArrayOf(styleableId))
        try {
            applyStyledAttributes(view, typedArray)
        } finally {
            typedArray.recycle()
        }
    }

    /**
     * Applies a TypedArray to a view.
     */
    fun applyStyledAttributes(view: View, typedArray: TypedArray) {
        // Find all methods annotated with @Attr
        val methods = view.javaClass.declaredMethods
        for (method in methods) {
            val attrAnnotation = method.getAnnotation(Attr::class.java) ?: continue
            val attrId = attrAnnotation.value
            
            if (typedArray.hasValue(attrId)) {
                applyAttribute(view, method, attrId, typedArray)
            }
        }
        
        // Find all fields annotated with @StyleableChild
        val fields = view.javaClass.declaredFields
        for (field in fields) {
            val styleableChildAnnotation = field.getAnnotation(StyleableChild::class.java) ?: continue
            val attrId = styleableChildAnnotation.value
            
            if (typedArray.hasValue(attrId)) {
                applyStyleToChild(view, field, attrId, typedArray)
            }
        }
    }

    /**
     * Applies a map of attributes to a view.
     */
    fun applyAttributes(view: View, attributes: Map<Int, Any?>) {
        // Find all methods annotated with @Attr
        val methods = view.javaClass.declaredMethods
        for (method in methods) {
            val attrAnnotation = method.getAnnotation(Attr::class.java) ?: continue
            val attrId = attrAnnotation.value
            
            if (attributes.containsKey(attrId)) {
                applyAttribute(view, method, attrId, attributes[attrId])
            }
        }
        
        // Find all fields annotated with @StyleableChild
        val fields = view.javaClass.declaredFields
        for (field in fields) {
            val styleableChildAnnotation = field.getAnnotation(StyleableChild::class.java) ?: continue
            val attrId = styleableChildAnnotation.value
            
            if (attributes.containsKey(attrId)) {
                applyStyleToChild(view, field, attrId, attributes[attrId])
            }
        }
    }

    private fun applyAttribute(view: View, method: Method, attrId: Int, typedArray: TypedArray) {
        method.isAccessible = true
        
        val parameterType = method.parameterTypes.firstOrNull() ?: return
        
        when (parameterType) {
            String::class.java -> {
                val value = typedArray.getString(attrId)
                if (value != null) {
                    method.invoke(view, value)
                }
            }
            Int::class.java, Int::class.javaPrimitiveType -> {
                val value = typedArray.getInt(attrId, 0)
                method.invoke(view, value)
            }
            Float::class.java, Float::class.javaPrimitiveType -> {
                val value = typedArray.getFloat(attrId, 0f)
                method.invoke(view, value)
            }
            Boolean::class.java, Boolean::class.javaPrimitiveType -> {
                val value = typedArray.getBoolean(attrId, false)
                method.invoke(view, value)
            }
            Drawable::class.java -> {
                val value = typedArray.getDrawable(attrId)
                if (value != null) {
                    method.invoke(view, value)
                }
            }
            else -> {
                // Try to get a resource ID
                val value = typedArray.getResourceId(attrId, 0)
                if (value != 0) {
                    method.invoke(view, value)
                }
            }
        }
    }

    private fun applyAttribute(view: View, method: Method, attrId: Int, value: Any?) {
        if (value == null) return
        
        method.isAccessible = true
        
        try {
            method.invoke(view, value)
        } catch (e: Exception) {
            // Try to convert the value if needed
            val parameterType = method.parameterTypes.firstOrNull() ?: return
            
            when {
                parameterType == Int::class.java && value is Number -> {
                    method.invoke(view, value.toInt())
                }
                parameterType == Float::class.java && value is Number -> {
                    method.invoke(view, value.toFloat())
                }
                parameterType == Boolean::class.java && value is String -> {
                    method.invoke(view, value.toBoolean())
                }
                parameterType == Drawable::class.java && value is Int -> {
                    val drawable = ContextCompat.getDrawable(view.context, value)
                    if (drawable != null) {
                        method.invoke(view, drawable)
                    }
                }
            }
        }
    }

    private fun applyStyleToChild(view: View, field: Field, attrId: Int, typedArray: TypedArray) {
        field.isAccessible = true
        
        val styleRes = typedArray.getResourceId(attrId, 0)
        if (styleRes != 0) {
            val childView = field.get(view) as? View ?: return
            childView.style(styleRes)
        }
    }

    private fun applyStyleToChild(view: View, field: Field, attrId: Int, value: Any?) {
        field.isAccessible = true
        
        when (value) {
            is Int -> {
                val childView = field.get(view) as? View ?: return
                childView.style(value)
            }
            is Style -> {
                val childView = field.get(view) as? View ?: return
                childView.style(value)
            }
        }
    }
}
